package com.eunion.web.common.aspect;

import java.lang.reflect.Method;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import com.eunion.core.IpUtils;
import com.eunion.core.common.SessionUser;
import com.eunion.web.common.annotation.MethodLog;
import com.eunion.web.common.vo.Web;

/**
 * \ Aspect 实现spring aop 切面（Aspect）：
 * 一个关注点的模块化，这个关注点可能会横切多个对象。事务管理是J2EE应用中一个关于横切关注点的很好的例子。 在Spring
 * AOP中，切面可以使用通用类（基于模式的风格） 或者在普通类中以 @Aspect 注解（@AspectJ风格）来实现。
 * 
 * AOP代理（AOP Proxy）： AOP框架创建的对象，用来实现切面契约（aspect contract）（包括通知方法执行等功能）。
 * 在Spring中，AOP代理可以是JDK动态代理或者CGLIB代理。 注意：Spring 2.0最新引入的基于模式（schema-based
 * ）风格和@AspectJ注解风格的切面声明，对于使用这些风格的用户来说，代理的创建是透明的。
 * 
 * 
 * <!-- aop -->
 * <bean id="logService" class="com.wssys.framework.LogService"></bean>
 * 
 * <!-- 启动对@AspectJ注解的支持 --> <aop:aspectj-autoproxy proxy-target-class="true" />
 * 
 */
@Component
@Aspect
public class LogAspect {

	private Logger logger = LoggerFactory.getLogger(getClass());

	@Autowired
	private HttpSession httpSession;

//	@Autowired
//	private SysLogService sysLogService;
//
//	@Autowired
//	private SysLogDetailService sysLogDetailService;

	/**
	 * 获取user session.
	 */
	private SessionUser getSessionUser() {
		try {
			return (SessionUser) httpSession.getAttribute(Web.SESSION_USER);
		} catch (IllegalStateException e) {
			logger.warn("session already invalidated {}", e.toString());
		}
		return null;
	}

	// @Autowired
	/**
	 * 在Spring
	 * 2.0中，Pointcut的定义包括两个部分：Pointcut表示式(expression)和Pointcut签名(signature
	 * )。让我们先看看execution表示式的格式：
	 * 括号中各个pattern分别表示修饰符匹配（modifier-pattern?）、返回值匹配（ret
	 * -type-pattern）、类路径匹配（declaring
	 * -type-pattern?）、方法名匹配（name-pattern）、参数匹配（(param
	 * -pattern)）、异常类型匹配（throws-pattern?），其中后面跟着“?”的是可选项。
	 * 
	 * @param point
	 * @throws Throwable
	 */

	@Pointcut("@annotation(c.n.z.e.common.annotation.MethodLog)")
	public void methodCachePointcut() {

	}

	// // @Before("execution(* com.wssys.controller.*(..))")
	// public void logAll(JoinPoint point) throws Throwable {
	// System.out.println("打印========================");
	// }
	//
	// // @After("execution(* com.wssys.controller.*(..))")
	// public void after() {
	// System.out.println("after");
	// }

	// 方法执行的前后调用
	// @Around("execution(* com.wssys.controller.*(..))||execution(*
	// com.bpm.*.web.account.*.*(..))")
	// @Around("execution(* com.wssys.controller.*(..))")
	// @Around("execution(*
	// org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.handle(..))")
	// @Around("methodCachePointcut()")
	@Around("execution(* c.n.z.web..*(..))")
	public Object around(ProceedingJoinPoint point) throws Throwable {

		Object object;
		Object[] method_param = null;

		String monthRemark = getMthodRemark(point);// methodLog remark
		SessionUser user = null;
		if (StringUtils.isNotEmpty(monthRemark)) {
			user = getSessionUser();
			logger.debug("before {} point User is :{}",monthRemark,user);
		}
		try {
			method_param = point.getArgs(); // 获取方法参数
			// String param=(String) point.proceed(point.getArgs());
			object = point.proceed();
		} catch (Exception e) {
			// 异常处理记录日志..log.error(e);
			throw e;
		}

		try {
			if (StringUtils.isNotEmpty(monthRemark)) {
				logger.debug("log aspect Methodlog {}", monthRemark);
				HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
						.getRequest();
				String ips = getIpAddress(request);
				long ip = IpUtils.ipToLong(ips);
				logger.debug("ip is {} long is :{}",ips,ip);
				if(null == user){
					user = getSessionUser();
					logger.debug("after {} point User is :{}",monthRemark,user);
				}
				
//				SysLog log = new SysLog();
//				log.setIp(ip);
//				log.setTag(monthRemark);
//				log.setTimeCreate(new Date().getTime());
//				String userEmail = "";
//				SysLogDetail sdLog = new SysLogDetail();
//				if (user != null && null != user.getSessionUserId()) {
//					log.setUserId(user.getSessionUserId());
//					userEmail = user.getSessionUserEmail();
//				}
//				log = sysLogService.save(log);
//				if (log != null && log.getId() != 0l) {
//					logger.debug("log aspect Methodlog ok");
//					sdLog.setLogId(log.getId());
//					String monthName = point.getSignature().getName();
//					String packages = point.getThis().getClass().getName();
//					if (packages.indexOf("$$EnhancerByCGLIB$$") > -1) { // 如果是CGLIB动态生成的类
//						try {
//							packages = packages.substring(0, packages.indexOf("$$"));
//						} catch (Exception ex) {
//							ex.printStackTrace();
//						}
//					}
//					sdLog.setRemark(packages.concat("。").concat(monthName));
//					if (null != method_param) {
//						if(StringUtils.isEmpty(userEmail)){
//							for(Object o : method_param){
//								if(o instanceof AuthUserBo){
//									AuthUserBo vo = (AuthUserBo) o;
//									if(vo != null && StringUtils.isNotEmpty(vo.getEmail())){
//										userEmail = vo.getEmail();
//									}
//								}
//								
//								
//							}
//						}
//						sdLog.setRemark(userEmail.concat(sdLog.getRemark().concat(Arrays.asList(method_param).toString())));
//					}
//					sysLogDetailService.save(sdLog);
//					logger.debug("log aspect Methodlog detail ok");
//				}

			}
		} catch (Exception e) {
			logger.error("log aspect Methodlog error", e);
		}

		return object;
	}

	// 方法运行出现异常时调用
	// @AfterThrowing(pointcut = "execution(* com.wssys.controller.*(..))",
	// throwing = "ex")
	public void afterThrowing(Exception ex) {
		logger.error("afterThrowing", ex);
	}

	// 获取方法的中文备注____用于记录用户的操作日志描述
	public static String getMthodRemark(ProceedingJoinPoint joinPoint) throws Exception {
		String targetName = joinPoint.getTarget().getClass().getName();
		String methodName = joinPoint.getSignature().getName();
		Object[] arguments = joinPoint.getArgs();
		Class targetClass = Class.forName(targetName);
		Method[] method = targetClass.getMethods();
		String remark = "";
		for (Method m : method) {
			if (m.getName().equals(methodName)) {
				Class[] tmpCs = m.getParameterTypes();
				if (tmpCs.length == arguments.length) {
					MethodLog methodCache = m.getAnnotation(MethodLog.class);
					if (methodCache != null) {
						remark = methodCache.remark();
					}
					break;
				}
			}
		}
		return remark;
	}

	/**
	 * 获取用户真实IP地址，不使用request.getRemoteAddr();的原因是有可能用户使用了代理软件方式避免真实IP地址,
	 * 
	 * 可是，如果通过了多级反向代理的话，X-Forwarded-For的值并不止一个，而是一串IP值，究竟哪个才是真正的用户端的真实IP呢？
	 * 答案是取X-Forwarded-For中第一个非unknown的有效IP字符串。
	 * 
	 * 如：X-Forwarded-For：192.168.1.110, 192.168.1.120, 192.168.1.130,
	 * 192.168.1.100
	 * 
	 * 用户真实IP为： 192.168.1.110
	 * 
	 * @param request
	 * @return
	 */
	public static String getIpAddress(HttpServletRequest request) {
		String ip = request.getHeader("x-forwarded-for");
		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
			ip = request.getHeader("Proxy-Client-IP");
		}
		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
			ip = request.getHeader("WL-Proxy-Client-IP");
		}
		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
			ip = request.getHeader("HTTP_CLIENT_IP");
		}
		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
			ip = request.getHeader("HTTP_X_FORWARDED_FOR");
		}
		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
			ip = request.getRemoteAddr();
		}
		
		if(ip != null && ip.equals("0:0:0:0:0:0:0:1")){
			ip = "127.0.0.1";
		}
		
		return ip;
	}

}